package main

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"runtime"
)

func randomPort() int {
	b := make([]byte, 2)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		panic("Universe ran out of random")
	}
	ret := binary.BigEndian.Uint16(b)
	ret |= 2048 // Make sure it's out of the 1-1024 port range
	return int(ret)
}

func clientServer() error {
	caddr := &net.TCPAddr{net.ParseIP("::1"), randomPort()}
	saddr := &net.TCPAddr{net.ParseIP("::1"), randomPort()}

	ln, err := net.ListenTCP("tcp6", saddr)
	if err != nil {
		return err
	}
	defer ln.Close()

	ch := make(chan error, 1)

	go func() {
		client, err := net.DialTCP("tcp6", caddr, saddr)
		ch <- err
		if client != nil {
			client.Close()
		}
	}()

	client, err := ln.Accept()
	if err != nil {
		return err
	}
	client.Close()
	return <-ch
}

func clientClient() error {
	addr1 := &net.TCPAddr{net.ParseIP("::1"), randomPort()}
	addr2 := &net.TCPAddr{net.ParseIP("::1"), randomPort()}

	c := func(laddr, raddr *net.TCPAddr, ch chan error) {
		sock, err := net.DialTCP("tcp6", laddr, raddr)
		if err == nil {
			sock.Close()
		}
		ch <- err
	}

	ch := make(chan error, 2)

	go c(addr1, addr2, ch)
	go c(addr2, addr1, ch)

	err := <-ch
	if err != nil {
		return err
	}
	return <-ch
}

func main() {
	fmt.Println("%s/%s", runtime.GOOS, runtime.GOARCH)

	const numChecks = 100

	// Sanity check: verify that client-server connections work as expected.
	failures := 0
	for i := 0; i < numChecks; i++ {
		if err := clientServer(); err != nil {
			fmt.Println("Error during client-server test:", err)
			failures++
		}
	}

	fmt.Printf("%d/%d client-server checks failed\n", failures, numChecks)

	// Now for the real fun, simultaneous open.
	failures = 0
	for i := 0; i < numChecks; i++ {
		if err := clientClient(); err != nil {
			fmt.Println("Error during client-client test:", err)
			failures++
		}
	}

	fmt.Printf("%d/%d client-client checks failed\n", failures, numChecks)
}
